/*
配置通过对象进行映射
需要配置对象数据结构
*/
package conf

import (
	"context"
	"database/sql"
	"fmt"
	"sync"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"go.uber.org/zap"
)

//全局配置变量实例
var (
	global *Config
	db     *sql.DB
)

//访问配置方式
func C() *Config {
	if global == nil {
		panic("config is not configed")
	}
	return global
}

//设置配置方式
func SetGlobalConfig(conf *Config) {
	global = conf
}

//生成默认全局配置，即使用户配置文件中不填写也应该有默认的
func NewDefaultConfig() *Config {
	return &Config{
		App:   NewDefaultApp(),
		MySql: NewDefaultMysql(),
		Log:   NewDefaultLog(),
	}
}

type Config struct {
	App   *app
	MySql *mysql
	Log   *Log
}

//自身服务的配置
type app struct {
	Name string `toml:"name"`
	Host string `toml:"host"`
	Port string `toml:"port"`
	//需要保护敏感数据，写在数据库中
	Key string `toml:"key"`
}

func NewDefaultApp() *app {
	return &app{
		Name: "restful-api",
		Host: "127.0.0.1",
		Port: "8050",
		Key:  "default no key",
	}
}

func (a *app) Addr() string {
	return fmt.Sprintf("%s:%s", a.Host, a.Port)
}

//Mysql数据库配置
type mysql struct {
	Host     string `toml:"host"`
	Port     string `toml:"port"`
	Username string `toml:"username"`
	Password string `toml:"password"`
	Database string `toml:"database"`

	MaxOpenConn int `toml:"max_open_conn"`
	MaxIdleConn int `toml:"max_idle_conn"`
	MaxLifeTime int `toml:"max_life_time"`
	MaxIdleTime int `toml:"max_idle_time"`

	lock sync.Mutex
}

func NewDefaultMysql() *mysql {
	return &mysql{
		Host:        "127.0.0.1",
		Port:        "3306",
		Username:    "root",
		Password:    "123456",
		Database:    "restful_api",
		MaxOpenConn: 50,
		MaxIdleConn: 20,
		MaxLifeTime: 10 * 60,
		MaxIdleTime: 5 * 60,
	}
}

//获取一个连接池
func (m *mysql) getDBConn() (*sql.DB, error) {
	var err error
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8&multiStatements=true", m.Username, m.Password, m.Host, m.Port, m.Database)
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return nil, fmt.Errorf("connect to mysql<%s> error, %s", dsn, err.Error())
	}
	db.SetMaxOpenConns(m.MaxOpenConn)
	db.SetMaxIdleConns(m.MaxIdleConn)
	db.SetConnMaxLifetime(time.Second * time.Duration(m.MaxLifeTime))
	db.SetConnMaxIdleTime(time.Second * time.Duration(m.MaxIdleTime))
	//ping连接是否能使用
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	if err := db.PingContext(ctx); err != nil {
		return nil, fmt.Errorf("ping mysql<%s> error, %s", dsn, err.Error())
	}
	return db, nil
}

//goroutine上锁,确保单例,并且在第一次使用建立连接池
func (m *mysql) GetDB() (*sql.DB, error) {
	m.lock.Lock()
	defer m.lock.Unlock()
	if db == nil {
		conn, err := m.getDBConn()
		if err != nil {
			return nil, fmt.Errorf("获取连接池失败,%s", err)
		}
		db = conn
	}
	return db, nil
}

//log配置
type Log struct {
	Level  string    `toml:"level" env:"LOG_LEVEL"`
	Dir    string    `toml:"dir" env:"LOG_PATH_DIR"`
	Format LogFormat `toml:"format" env:"LOG_FORMAT"`
	To     LogTo     `toml:"to" env:"LOG_TO"`
}

func NewDefaultLog() *Log {
	return &Log{
		//Level: "debug",
		Level:  zap.DebugLevel.String(),
		Format: TextFormat,
		To:     ToStdout,
	}
}
